package org.example.netty.zk;

import java.util.HashSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 分布式ID
 * SnowflakeID生成器
 * WorkerID == 机器ID
 */
public class SnowflakeIdGenerator {

    /**
     * 时间从 2017-01-01 00:00:00开始
     */
    private static final long START_TIME = 1483171200000L;

    /**
     * 机器ID 13位
     */
    private static final int WORK_ID_BITS = 13;

    /**
     * 序号列10位
     */
    private static final int SEQUENCE_BITS = 10;

    /**
     * 最大机器编号
     */
    private static final long MAX_WORKID = ~(-1l << WORK_ID_BITS);

    /**
     * 最大序号节点
     */
    private static final long MAX_SEQUENCE = ~(-1l << SEQUENCE_BITS);

    /**
     * 节点编号
     */
    public long seqId = 0;

    /**
     * 机器编号
     */
    public long workId = 0;

    /**
     * 上一个时间戳
     */
    private long lastTimestap = -1;

    public static SnowflakeIdGenerator instance = new SnowflakeIdGenerator();

    public SnowflakeIdGenerator() {

    }

    public void initWorkerId(long workId) {
        if (workId > MAX_WORKID) {
            throw new RuntimeException("wordId已经超过了最大值：" + MAX_WORKID);
        }
        instance.workId = workId;
    }

    /**
     * 获取下一个ID
     * @return
     */
    public long nextId() {
        return generateId();
    }

    /**
     * 生成ID
     * @return
     */
    public synchronized long generateId() {
        long current = System.currentTimeMillis();

        if (current < lastTimestap) {
            throw new RuntimeException("发生机器时钟回拨");
        }

        if (current == lastTimestap) {
            seqId = (seqId + 1) & MAX_SEQUENCE;
            if (seqId == MAX_SEQUENCE) {
                // TODO seqId已经满了，阻塞等到到下一个节点
                current = waitTimestamp(lastTimestap);
            }
        }
        else {
            // TODO 已经进入下一个时间戳，seqId从0开始
            seqId = 0l;
        }

        long time = (current - START_TIME) << (WORK_ID_BITS + SEQUENCE_BITS);
        long tempWorkId = workId << SEQUENCE_BITS;

        // TODO 保存本地时间戳
        lastTimestap = current;

        return time | tempWorkId | seqId;
    }

    /**
     * 循环等待
     * @param nextStamp
     */
    public long waitTimestamp(long nextStamp) {
        long current = System.currentTimeMillis();
        while (current <= nextStamp) {
            current = System.currentTimeMillis();
        }
        return current;
    }

    public static void main(String[] args) {
        // TODO 8191
        System.out.println(~(-1l<<13));

        System.out.println((1<<13) - 1);

        // TODO 负数打印出来的就是补码
        System.out.println("负-1 = " + Long.toBinaryString(-1l));
        // TODO 左移13位，右侧补0
        System.out.println("负-1 = " + Long.toBinaryString(-1l<<13));
        // TODO 补码取反
        System.out.println("负-1 = " + Long.toBinaryString(~(-1l<<13)));

        System.out.println("正1 = " + Long.toBinaryString(1l));

        SnowflakeIdGenerator generator = SnowflakeIdGenerator.instance;
        generator.initWorkerId(112);

        ExecutorService service = Executors.newFixedThreadPool(10);
        HashSet<Long> set = new HashSet<>();
        long startMillis = System.currentTimeMillis();
        System.out.println("生产开始");
        for (int i = 0; i < 10; i++) {
            service.submit(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 50; j++) {
                        long seqId = generator.nextId();
                        synchronized (set) {
                            set.add(seqId);
                        }
                    }
                }
            });
        }

        // TODO 结束线程
        service.shutdown();

        try {
            service.awaitTermination(1000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("生产结束");
        long end = System.currentTimeMillis();
        System.out.println("耗时：" + (end - startMillis));
    }
}
